package fem2d;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

import math2.ConstantFunctionRnToR;
import math2.FunctionRnToR;

import org.junit.Test;

import fem2.AbstractStructuralStaticDemo;
import fem2.Constraint;
import fem2.Debugger;
import fem2.DistributedLoad;
import fem2.Edge;
import fem2.Element;
import fem2.Load;
import fem2.MaterialModel;
import fem2.Mesh;
import fem2.MeshGenerator;
import fem2.MeshPart;
import fem2.MeshUtilities;
import fem2.Model;
import fem2.Node;
import fem2.Observer;
import fem2.StringUtilities;
import fem2.Topology;
import fem2.analysis.Analysis;
import fem2.element.StructuralElement;
import fem2.enu.EchoLevelType;
import fem2.enu.SortingCriteria;
import fem2.enu.State;
import fem2.level_set.Crack;
import fem2.material.StVenantKirchhoffMM;
import fem2.observer.GidDisplacementObserver;
import fem2.observer.TimeStepObserver;
import fem2.observer.TimingObserver;
import fem2.observer.XfemManager;
import fem2.pre_and_post.GidPostStructural;
import fem2.pre_and_post.GmshMeshGenerator;
import fem2.strategies.NonAdaptiveNewtonRaphsonStrategyForXfem;
import fem2.strategies.Strategy;

/**
 * Edge crack problem. Example taken from
 * 
 * <pre>
 * Extended finite element method with edge-based strain smoothing (ESm-XFEM) for linear elastic crack growth
 * L. Chen , T. Rabczuk , S.P.A. Bordas , G.R. Liu , K.Y. Zeng , P. Kerfriden
 * </pre>
 * 
 * @author hbui
 * 
 */
public class EdgeCrackUnderTensionCase1 extends AbstractStructuralStaticDemo {

	public EdgeCrackUnderTensionCase1() {
		projectDir = "/home/hbui/kratos_janosch";
		projectName = "EdgeCrack.gid";
		meshFileName = "EgdeCrack.gmsh.msh";
		// meshFileName = "EdgeCrack.msh";
	}

	@Override
	public Mesh createMesh() {
		String fn = projectDir + '/' + projectName + '/' + meshFileName;
		MeshGenerator mg = new GmshMeshGenerator(fn);
		// MeshGenerator mg = new GidMeshGenerator(fn);
		Mesh mesh = mg.getMesh(2);
		return mesh;
	}

	@Test
	public void test1() {
		Mesh mesh = createMesh();
		try {
			MeshUtilities.writeGiDMesh(mesh,
					"/home/hbui/kratos_janosch/EdgeCrack.gid/EdgeCrack.msh");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public Model createConditions(Model m) {
		Mesh mesh = m.getMesh();

		Constraint cx = new Constraint(false, true);
		Constraint cy = new Constraint(true, false);

		ArrayList<Node> bottom = MeshUtilities.seekNodesOnLine(mesh, 0, 0, 1, 0);
		mesh.addConstraint(cy, bottom);

		MeshUtilities.seekNode(mesh, 0, 0).addConstraint(cx);

		ArrayList<Node> top = MeshUtilities.seekNodesOnLine(mesh, 0, 2, 1, 0);
		mesh.setSortingCriteria(SortingCriteria.SORT_ON_X_COORDINATE, top);
		Collections.sort(top);
		mesh.setSortingCriteria(SortingCriteria.SORT_ON_ID, top);

		FunctionRnToR t = new ConstantFunctionRnToR(1.0);
		for (int i = 0; i < top.size() - 1; i++) {
			Node n1 = top.get(i);
			Node n2 = top.get(i + 1);
			Edge e = mesh.addEdge(new Edge(n1, n2));
			e.setNumberOfFields(2);

			Load l = new DistributedLoad(e, null, t);
			m.addLoad(l);
		}

		return m;
	}

	@Override
	public MaterialModel createMaterial(Model m) {
		double E = 3e7;
		double nu = 0.3;
		double t = 1;
		State ss = State.PLANE_STRAIN;
		MaterialModel mm = new StVenantKirchhoffMM(E, nu, t, 0, ss);
		return mm;
	}

	@Override
	public Element createElement(MeshPart mp, MaterialModel mm) {
		return new StructuralElement(mp, mm);
	}

	@Override
	protected void addFundamentalObservers(Model m, Strategy s, TimeStepObserver o) throws Error {
		if (meshFileName == null) {
			throw new Error("mesh file name was not set");
		}

		/*
		 * clean all previous post files
		 */
		String meshName = StringUtilities.getFileName(meshFileName);

		GidPostStructural post = new GidPostStructural(projectDir, projectName, meshName);
		post.clearAllPostFiles();

		Observer do1 = new GidDisplacementObserver(m, o, projectDir, projectName, meshName);

		s.addObserver(do1);
		s.addObserver(new TimingObserver());
	}

	@Override
	public void run(int nt) {
		Model m = createModel();

		Topology to = new Topology(m);
		m.getMesh().setTopology(to);

		Analysis an = new Analysis(m);
		an.setStrategy(new NonAdaptiveNewtonRaphsonStrategyForXfem(m, nt));
		Strategy s = an.getStrategy();
		s.setEchoLevel(EchoLevelType.OUTPUT, EchoLevelType.AUX1);

		double[] tip_start = new double[] { 0, 1 };
		double[] tip_delta = new double[] { 0.3, 0 };

		Crack c = new Crack(m, tip_start, tip_delta);
		c.setJIntegralRadius(0.3);
		c.setPropagationStep(0.1);

		XfemManager x = new XfemManager(m);

		x.addCrack(c);

		s.addObserver(x);

		addFundamentalObservers(m, s, new TimeStepObserver(s));

		an.run();

		// postProcess(m);
		//
		// Node n0 = m.getMesh().getNode(0);
		// Debugger.watch(n0.getUHatLocal());
		//
		// Face f = m.getMesh().getFace(0);
		// double[] uh = f.getUHatLocal();
		// FunctionBasisOnRnToR basis = f.getBasis();
		// double[] u0 = new double[2];
		// for (int i = 0; i < basis.getDimension(); i++) {
		// u0[0] += basis.getBasisFunction(i).valueAt(-1, -1) * uh[2 * i];
		// u0[1] += basis.getBasisFunction(i).valueAt(-1, -1) * uh[2 * i + 1];
		// }
		// Debugger.watch(u0);

		Debugger.warn("Analysis completed");
	}

	public static void main(String[] args) {
		AbstractStructuralStaticDemo demo = new EdgeCrackUnderTensionCase1();

		demo.run(5);

	}
}
